package com.xishu.dao;

import com.alibaba.fastjson.JSON;
import com.xishu.config.Config;
import com.xishu.dao.jpa.*;
import com.xishu.entity.AbstractEntity;
import com.xishu.entity.Company;
import com.xishu.entity.ShopItem;
import com.xishu.entity.User;
import com.xishu.entity.activity.Activity;
import com.xishu.entity.area.Area;
import com.xishu.entity.comment.Comment;
import com.xishu.entity.config.BackConfig;
import com.xishu.entity.customer.*;
import com.xishu.entity.eft.EftConfig;
import com.xishu.entity.feedback.Feedback;
import com.xishu.entity.foodPandaOrder.FoodPandaOrder;
import com.xishu.entity.foodpanda.Category;
import com.xishu.entity.foodpanda.Menu;
import com.xishu.entity.foodpanda.Product;
import com.xishu.entity.hardware.LabelToPrinter;
import com.xishu.entity.hardware.Printer;
import com.xishu.entity.hardware.PrinterFailedResult;
import com.xishu.entity.hardware.PrinterLabel;
import com.xishu.entity.member.Member;
import com.xishu.entity.oplog.OpLog;
import com.xishu.entity.oplog.OrderOpLog;
import com.xishu.entity.order.Order;
import com.xishu.entity.order.*;
import com.xishu.entity.payment.PaymentCheck;
import com.xishu.entity.plat.*;
import com.xishu.entity.shop.*;
import com.xishu.entity.takeout.TakeOutConfig;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.TakeOutSettingDao;
import com.xishu.util.BeanUtil;
import com.xishu.util.ClassUtil;
import net.sf.json.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.util.*;

public class DaoUtil {
    private static Logger logger = LoggerFactory.getLogger(DaoUtil.class);

    private static DaoUtil instance = new DaoUtil();
    //JpaSpecificationExecutor JpaRepository 一定是成双成对的出现
    private Map<String, JpaSpecificationExecutor> daoMap = new HashMap<>();
    private Config config = Config.getInstance();

    private DaoUtil() {
        daoMap.put(User.class.getName(), BeanUtil.getBean(UserDao.class));
        daoMap.put(OpLog.class.getName(), BeanUtil.getBean(OplogDao.class));
        daoMap.put(Order.class.getName(), BeanUtil.getBean(OrderDao.class));
        daoMap.put(Shop.class.getName(), BeanUtil.getBean(ShopDao.class));
        daoMap.put(PrinterLabel.class.getName(), BeanUtil.getBean(PrinterLabelDao.class));
        daoMap.put(FoodTableTypeConfig.class.getName(), BeanUtil.getBean(FoodTableTypeConfigDao.class));
        daoMap.put(Catalog.class.getName(), BeanUtil.getBean(CatalogDao.class));
        daoMap.put(Scene.class.getName(), BeanUtil.getBean(SceneDao.class));
        daoMap.put(Company.class.getName(), BeanUtil.getBean(CompanyDao.class));
        daoMap.put(Item.class.getName(), BeanUtil.getBean(ItemDao.class));
        daoMap.put(DiscountTicket.class.getName(), BeanUtil.getBean(DiscountTicketDao.class));
        daoMap.put(FoodTable.class.getName(), BeanUtil.getBean(FoodTableDao.class));
        daoMap.put(CatalogItemMapping.class.getName(), BeanUtil.getBean(CatalogItemMappingDao.class));
        daoMap.put(Feedback.class.getName(), BeanUtil.getBean(FeedbackDao.class));
        daoMap.put(LineUpConfig.class.getName(), BeanUtil.getBean(LineUpConfigDao.class));
        daoMap.put(TakeOutConfig.class.getName(), BeanUtil.getBean(TakeOutConfigDao.class));
        daoMap.put(OrderItemMapping.class.getName(), BeanUtil.getBean(OrderItemMappingDao.class));
        daoMap.put(LineUp.class.getName(), BeanUtil.getBean(LineUpDao.class));
        daoMap.put(Area.class.getName(), BeanUtil.getBean(AreaDao.class));
        daoMap.put(UserShopMapping.class.getName(), BeanUtil.getBean(UserShopMappingDao.class));
        daoMap.put(UserCompanyMapping.class.getName(), BeanUtil.getBean(UserCompanyMappingDao.class));
        daoMap.put(RunModel.class.getName(), BeanUtil.getBean(RunModelDao.class));
        daoMap.put(BackConfig.class.getName(), BeanUtil.getBean(BackConfigDao.class));
        daoMap.put(OrderCatalogMapping.class.getName(), BeanUtil.getBean(OrderCatalogMappingDao.class));
        daoMap.put(OrderPayTypeMapping.class.getName(), BeanUtil.getBean(OrderPayTypeMappingDao.class));
        daoMap.put(SceneCatalogMapping.class.getName(), BeanUtil.getBean(SceneCatalogMappingDao.class));
        daoMap.put(FoodTableConfig.class.getName(), BeanUtil.getBean(FoodTableConfigDao.class));
        daoMap.put(Printer.class.getName(), BeanUtil.getBean(PrinterDao.class));
        daoMap.put(ShopTicketMapping.class.getName(), BeanUtil.getBean(ShopTicketMappingDao.class));
        daoMap.put(PrivilegeMapping.class.getName(), BeanUtil.getBean(PrivilegeMappingDao.class));
        daoMap.put(OrderTypeMapping.class.getName(), BeanUtil.getBean(OrderTypeMappingDao.class));
        daoMap.put(OrderWaiterMapping.class.getName(), BeanUtil.getBean(OrderWaiterMappingDao.class));
        daoMap.put(OrderDiscountTicketMapping.class.getName(), BeanUtil.getBean(OrderDiscountTicketMappingDao.class));
        daoMap.put(GlobalSetting.class.getName(), BeanUtil.getBean(GlobalSettingDao.class));
        daoMap.put(Reason.class.getName(), BeanUtil.getBean(ReasonDao.class));
        daoMap.put(ShopFlag.class.getName(), BeanUtil.getBean(ShopFlagDao.class));
        daoMap.put(TouchDevice.class.getName(), BeanUtil.getBean(TouchDeviceDao.class));
        daoMap.put(GuideConfig.class.getName(), BeanUtil.getBean(GuideConfigDao.class));
        daoMap.put(PrinterFailedResult.class.getName(), BeanUtil.getBean(PrinterFailedResultDao.class));
        daoMap.put(ItemDetailIndex.class.getName(), BeanUtil.getBean(ItemDetailIndexDao.class));
        daoMap.put(CachePlatReport.class.getName(), BeanUtil.getBean(CachePlatReportDao.class));
        daoMap.put(ReportData.class.getName(), BeanUtil.getBean(ReportDataDao.class));
        daoMap.put(OrderNo.class.getName(), BeanUtil.getBean(OrderNoDao.class));
        daoMap.put(DeleteOrderItem.class.getName(), BeanUtil.getBean(DeleteOrderItemDao.class));
        daoMap.put(DayReport.class.getName(), BeanUtil.getBean(DayReportDao.class));
        daoMap.put(LabelToPrinter.class.getName(), BeanUtil.getBean(LabelToPrinterDao.class));
        daoMap.put(OrderServiceFeeMapping.class.getName(), BeanUtil.getBean(OrderServiceFeeMappingDao.class));
        daoMap.put(OrderPackingFeeMapping.class.getName(), BeanUtil.getBean(OrderPackingFeeMappingDao.class));
        daoMap.put(OrderTakeOutDiscountFeeMapping.class.getName(), BeanUtil.getBean(OrderTakeOutDiscountFeeMappingDao.class));
        daoMap.put(Customer.class.getName(), BeanUtil.getBean(CustomerDao.class));
        daoMap.put(CustomerTicket.class.getName(), BeanUtil.getBean(CustomerTicketDao.class));
        daoMap.put(MarketBirthdayTemplate.class.getName(), BeanUtil.getBean(MarketBirthdayTemplateDao.class));
        daoMap.put(MarketEmailTemplate.class.getName(), BeanUtil.getBean(MarketEmailTemplateDao.class));
        daoMap.put(MarketHistory.class.getName(), BeanUtil.getBean(MarketHistoryDao.class));
        daoMap.put(MarketSms.class.getName(), BeanUtil.getBean(MarketSmsDao.class));
        daoMap.put(MarketTask.class.getName(), BeanUtil.getBean(MarketTaskDao.class));
        daoMap.put(Quota.class.getName(), BeanUtil.getBean(QuotaDao.class));
        daoMap.put(ScoreDiscountTicket.class.getName(), BeanUtil.getBean(ScoreDiscountTicketDao.class));
        daoMap.put(ScoreHistory.class.getName(), BeanUtil.getBean(ScoreHistoryDao.class));
        daoMap.put(ScoreShop.class.getName(), BeanUtil.getBean(ScoreShopDao.class));
        daoMap.put(VipLevel.class.getName(), BeanUtil.getBean(VipLevelDao.class));
        daoMap.put(VipLevelTicketConfig.class.getName(), BeanUtil.getBean(VipLevelTicketConfigDao.class));
        daoMap.put(VipRenewEmailTemplate.class.getName(), BeanUtil.getBean(VipRenewEmailTemplateDao.class));
        daoMap.put(VipRenewMode.class.getName(), BeanUtil.getBean(VipRenewModeDao.class));
        daoMap.put(CustomerOrder.class.getName(), BeanUtil.getBean(CustomerOrderDao.class));
        daoMap.put(CompanyMainItem.class.getName(), BeanUtil.getBean(CompanyMainItemDao.class));
        daoMap.put(TaskExecuteHistory.class.getName(), BeanUtil.getBean(TaskExecuteHistoryDao.class));
        daoMap.put(SendHistory.class.getName(), BeanUtil.getBean(SendHistoryDao.class));
        daoMap.put(NoticeHistory.class.getName(), BeanUtil.getBean(NoticeHistoryDao.class));
        daoMap.put(SendToKitchenAudit.class.getName(), BeanUtil.getBean(SendToKitchenAuditDao.class));
        daoMap.put(PayType.class.getName(), BeanUtil.getBean(PayTypeDao.class));
        daoMap.put(OrderItemNo.class.getName(), BeanUtil.getBean(OrderItemNoDao.class));
        daoMap.put(StripeLocation.class.getName(), BeanUtil.getBean(StripeAddressDao.class));
        daoMap.put(StripeReader.class.getName(), BeanUtil.getBean(StripeReaderDao.class));
        daoMap.put(PaymentCheck.class.getName(), BeanUtil.getBean(PaymentCheckDao.class));
        daoMap.put(TakeOutSetting.class.getName(), BeanUtil.getBean(TakeOutSettingDao.class));
        daoMap.put(Menu.class.getName(), BeanUtil.getBean(MenuDao.class));
        daoMap.put(Category.class.getName(), BeanUtil.getBean(CategoryDao.class));
        daoMap.put(Product.class.getName(), BeanUtil.getBean(ProductDao.class));
        daoMap.put(FoodPandaOrder.class.getName(),BeanUtil.getBean(FoodPandaOrderDao.class));
        daoMap.put(OrderOpLog.class.getName(),BeanUtil.getBean(OrderOplogDao.class));
        daoMap.put(EftConfig.class.getName(), BeanUtil.getBean(EftConfigDao.class));
        daoMap.put(Member.class.getName(), BeanUtil.getBean(MemberDao.class));
        daoMap.put(Activity.class.getName(), BeanUtil.getBean(ActivityDao.class));
        daoMap.put(ShopMessage.class.getName(), BeanUtil.getBean(ShopMessageDao.class));
        daoMap.put(ShopItem.class.getName(),BeanUtil.getBean(ShopItemDao.class));
        daoMap.put(Comment.class.getName(),BeanUtil.getBean(CommentDao.class));
        daoMap.put(Praise.class.getName(),BeanUtil.getBean(PraiseDao.class));
        daoMap.put(ShopSearchItem.class.getName(),BeanUtil.getBean(ShopSearchItemDao.class));
    }

    public static DaoUtil getInstance() {
        return instance;
    }

    /**
     * 获取DAO处理器
     *
     * @param t
     * @return
     * @tparam T
     */
    public <T> JpaSpecificationExecutor getDao(T t) {
        return daoMap.get(t.getClass().getName());
    }

    /**
     * 获取DAO处理器
     *
     * @param tclass
     * @return
     * @tparam T
     */
    public <T> JpaSpecificationExecutor getDao(Class<T> tclass) {
        return daoMap.get(tclass.getName());
    }

    /**
     * 获取通用处理器
     *
     * @param t
     * @param <T>
     * @return
     */
    public <T> JpaRepository getJpa(T t) {
        return (JpaRepository) daoMap.get(t.getClass().getName());
    }

    /**
     * 获取通用处理器
     *
     * @param tclass
     * @return
     */
    public <T> JpaRepository getJpa(Class<T> tclass) {
        return (JpaRepository) daoMap.get(tclass.getName());
    }

    /**
     * 分页查询
     *
     * @param page
     * @param tclass
     * @return
     * @tparam T
     */
    public <T> List<T> pageQueryInt(int page, Class<T> tclass) {
        PageRequest pageRequest = PageRequest.of(page, config.getShowPageSize());
        return pageQuery(pageRequest, tclass);
    }


    /**
     * 分页查询
     *
     * @param pageable
     * @param tclass
     * @return
     * @tparam T
     */
    public <T> List<T> pageQuery(Pageable pageable, Class<T> tclass) {
        JpaSpecificationExecutor jpaRepository = getDao(tclass);
        Page<T> page = ((JpaRepository) jpaRepository).findAll(pageable);

        if (page.getTotalElements() == 0) {
            return new ArrayList<>();
        } else {
            return page.getContent();
        }
    }

    /**
     * 删除实体
     *
     * @param t
     * @tparam T
     */
    public <T extends AbstractEntity> void deleteEntity(T t) {
        logger.info("will delete entity , {},{} ", t.getId(), t.getClass().getName());

        JpaRepository jpaRepository = getJpa(t);
        jpaRepository.deleteById(t.getId());
    }

    /**
     * 创建实体
     *
     * @param t
     * @tparam T
     */
    public <T> void createEntity(T t) {
        JpaRepository jpaRepository = getJpa(t);
        jpaRepository.save(t);
    }

    /**
     * 更新实体
     *
     * @param t
     * @tparam T
     */
    public <T> void updateEntity(T t) {
        JpaRepository jpaRepository = getJpa(t);
        jpaRepository.save(t);
    }

    /**
     * 通过ID查询实体
     *
     * @param id
     * @param tclass
     * @return
     * @tparam T
     */
    public <T extends AbstractEntity> T findById(long id, Class<T> tclass) {
        JpaRepository jpaRepository = getJpa(tclass);
        Optional<T> entity = jpaRepository.findById(id);

        if (entity.isPresent()) {
            return entity.get();
        } else {
            return null;
        }
    }

    /**
     * 查询实体，找不到则报错
     *
     * @param id
     * @param tclass
     * @return
     * @tparam T
     */
    public <T extends AbstractEntity> T fetchById(long id, Class<T> tclass) throws ResponseStatusException {
        T entity = findById(id, tclass);

        if (entity == null) {
            VerifyUtil.throwError(ResponseStatus.INVALID_PARAMETER);
        }

        return entity;
    }

    /**
     * 查询实体，找不到则报错
     *
     * @return
     */
    public <T extends AbstractEntity> T fetchById(T t) throws ResponseStatusException {
        if (t == null || t.getId() == null) {
            VerifyUtil.throwError(ResponseStatus.INVALID_PARAMETER);
        }

        T entity = (T) findById(t.getId(), t.getClass());

        if (entity == null) {
            VerifyUtil.throwError(ResponseStatus.INVALID_PARAMETER);
        }

        return entity;
    }

    /**
     * 查询列表
     *
     * @param specification
     * @param tclass
     * @return
     * @tparam T
     */
    public <T extends AbstractEntity> List<T> find(Specification<T> specification, Class<T> tclass) {
        JpaSpecificationExecutor jpa = getDao(tclass);
        return jpa.findAll(specification);
    }

    /**
     * 相等值
     *
     * @param key   key
     * @param value 过滤值
     * @return
     * @tparam T 类型
     */
    public <T> Specification<T> sqlEq(String key, Object value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(root.get(key), value);
            }
        };
    }

    /**
     * 判断某一个值是否存在
     *
     * @param key key
     * @return
     * @tparam T 类型
     */
    public <T> Specification<T> sqlExist(String key) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.isNotNull(root.get(key));
            }
        };
    }

    /**
     * 判断某一个值是否存在
     *
     * @param key key
     * @return
     * @tparam T 类型
     */
    public <T> Specification<T> sqlNull(String key) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.isNull(root.get(key));
            }
        };
    }

    /**
     * 在一个范围里面
     *
     * @param key   key
     * @param value 过滤值
     * @return
     * @tparam T 类型
     */
    public <T> Specification<T> sqlIn(String key, List<Object> value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return root.get(key).in(value);
            }
        };
    }

    /**
     * 比较大小Long
     *
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Specification sqlGte(String key, long value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.greaterThanOrEqualTo(root.get(key).as(Long.class), value);
            }
        };
    }


    /**
     * 比较大小Long
     *
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Specification sqlGt(String key, long value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.greaterThan(root.get(key).as(Long.class), value);
            }
        };
    }

    /**
     * 比较大小Long
     *
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Specification sqlGt(String key, int value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.greaterThan(root.get(key).as(Integer.class), value);
            }
        };
    }


    /**
     * 比较大小Double
     *
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Specification sqlGte(String key, double value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.greaterThanOrEqualTo(root.get(key).as(Double.class), value);
            }
        };
    }

    /**
     * 比较大小Long
     *
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Specification sqlGt(String key, double value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.greaterThan(root.get(key).as(Double.class), value);
            }
        };
    }

    /**
     * 比较大小Long
     *
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Specification sqlLte(String key, long value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.lessThanOrEqualTo(root.get(key).as(Long.class), value);
            }
        };
    }

    /**
     * 比较大小Double
     *
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Specification sqlLte(String key, double value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.lessThanOrEqualTo(root.get(key).as(Double.class), value);
            }
        };
    }

    /**
     * 前缀like查询，主要是走索引
     *
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Specification prefixLike(String key, Object value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.like(root.get(key), value.toString() + "%");
            }
        };
    }

    /**
     * 按位与操作
     *
     * @param key
     * @param value 2的倍数，从1开始，比如1，2，4，8
     * @param <T>
     * @return
     */
    public <T> Specification bitAnd(String key, int value) {
        //如果是1，那么直接对2取模后，再判断是否为1
        if (value == 1) {
            return new Specification<T>() {
                @Override
                public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    Expression<Integer> modExpress = criteriaBuilder.mod(root.get(key).as(Integer.class), 2);
                    return criteriaBuilder.equal(modExpress, 1);
                }
            };
        }

        Integer bitNumber = Double.valueOf(Math.pow(2, value)).intValue();
        Integer smallNumber = Double.valueOf(Math.pow(2, value - 1)).intValue();

        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //先按照大一点数求模，比如现在数字是3，那就是2的3次方，算出来是8，实现会先算大一倍的数，比如16的模，如果模比8大于或者等于，那么说明这个数是包含了8那一位
                Expression<Integer> modExpress = criteriaBuilder.mod(root.get(key).as(Integer.class), bitNumber);
                return criteriaBuilder.greaterThanOrEqualTo(modExpress, smallNumber);
            }
        };
    }

    /**
     * 相等值
     *
     * @param key   key
     * @param value 过滤值
     * @return
     * @tparam T 类型
     */
    public <T> Specification sqlLike(String key, Object value) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.like(root.get(key), "%" + value.toString() + "%");
            }
        };
    }

    /**
     * 多个条件取与
     *
     * @param list
     * @return
     */
    public <T> Specification and(List<Specification<T>> list) {
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }

        for (Specification<T> tSpecification : list) {
            logger.info("specification is {}", toString(tSpecification));
        }

        Specification<T> first = list.get(0);
        if (list.size() == 1) {
            return first;
        }

        List<Specification<T>> subList = list.subList(1, list.size());

        for (Specification specification : subList) {
            if (specification != null) {
                first = first.and(specification);
            }
        }

        logger.debug("specification is {}", toString(first));
        return first;
    }

    /**
     * 多条件求或
     *
     * @param list
     * @param <T>
     * @return
     */
    public <T> Specification or(List<Specification<T>> list) {
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }

        Specification<T> first = list.get(0);

        if (list.size() == 1) {
            logger.info("specification is {}", JSONObject.fromObject(first).toString());
            return first;
        }

        List<Specification<T>> subList = list.subList(1, list.size());

        for (Specification specification : subList) {
            if (specification != null) {
                first = first.or(specification);
            }
        }

//        logger.info("specification is {}", first.toString());
        return first;

    }

    public String toString(Specification specification)  {
        HashMap<String, String> map = new HashMap<>();

        try {
            for (Field field : ClassUtil.getAllField(specification)) {
                Object o = field.get(specification);
                if (o != null) {
                    map.put(field.getName(), o.toString());
                } else {
                    map.put(field.getName(), "");
                }
            }
        } catch (Exception e) {
            logger.error("e", e);
        }

        return JSON.toJSON(map).toString();
    }
}
